<?php

namespace App\Kuafu\Mybank\Service;

use App\Events\OperationLogEvent;
use App\Exceptions\BaseException;
use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Common\Model\OperationLogRecordModel;
use App\Kuafu\Mybank\Model\MybankEntryModel;
use App\Kuafu\Mybank\Model\MybankShopBindModel;
use App\Kuafu\Mybank\Resources\MybankEntryDetailResource;
use App\Kuafu\Mybank\Resources\MybankEntryListResource;
use App\Kuafu\Nsq\Enum\NsqCodeEnum;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Sms\Service\SmsService;
use App\Kuafu\User\Model\User;
use App\Library\OperationLog\OperationLogQuery;
use App\Remote\Mybank\MybankRegisterRemote;
use App\Remote\Mybank\MybankUploadRemote;
use App\Remote\Mybank\Remote;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
use Kuafu\Nsq\NsqPublisher;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class MybankEntryService extends BaseService
{

    /**
     * @param int $pageNum
     * @param int $pageSize
     * @param string $principalMobile
     * @param  $status
     * @param string $merchantType
     * @param string $merchantName
     * @param string $updateStartTime
     * @param string $updateEndTime
     * @param string $shopInfo
     * @return array|void
     * @throws BaseException
     */
    public static function index(
        int $pageNum,
        int $pageSize,
        string $principalMobile,
        $status,
        string $merchantType,
        string $merchantName,
        string $updateStartTime,
        string $updateEndTime,
        string $shopInfo
    ) {
        try {
            $pagination = MybankEntryModel::query()
                                          ->when(strlen($shopInfo), function (Builder $query) use ($shopInfo) {
                                              $shopLists = Shop::query()->where("shopCode", "like", "%{$shopInfo}%")
                                                               ->orWhere("name", "like", "%{$shopInfo}%")->get();
                                              if ($shopLists->isNotEmpty()) {
                                                  $shopIds = $shopLists->pluck("id")->toArray();
                                                  $query->whereHas("shopBindRel", function ($query) use ($shopIds) {
                                                      $query->whereIn("shopId", $shopIds)->where("isDeleted", 0);
                                                  });
                                              } else {
                                                  $query->whereHas("shopBindRel", function ($query) {
                                                      $query->where("shopId", -1)->where("isDeleted", 0);
                                                  });
                                              }
                                          })
                                          ->when(strlen($principalMobile), function ($query) use ($principalMobile) {
                                              $query->where('principalMobile', 'like', "%{$principalMobile}%");
                                          })
                                          ->with([
                                              'shopBindInfoRel' => function ($query) {
                                                  $query->where('mybank_shop_bind.isDeleted', 0);
                                              }
                                          ])
                                          ->when(strlen($merchantName), function ($query) use ($merchantName) {
                                              $query->where('merchantName', 'like', "%{$merchantName}%")

                                                  // 个体工商户查负责人名称
                                                    ->orWhere(function ($qq) use ($merchantName) {
                                                      $qq->where([
                                                          'merchantType' => MybankEntryModel::MERCHANT_TYPE_02,
                                                          'contactName'  => $merchantName
                                                      ]);
                                                  });
                                          })
                                          ->when(
                                              strlen($status) &&
                                              in_array($status, array_keys(MybankEntryModel::STATUS_TEXT)),
                                              function ($query) use ($status) {
                                                  $query->where('status', $status);
                                              }
                                          )
                                          ->when(
                                              in_array($merchantType, array_keys(MybankEntryModel::MERCHANT_TYPE_TEXT),
                                                  true),
                                              function ($query) use ($merchantType) {
                                                  $query->where('merchantType', $merchantType);
                                              }
                                          )
                                          ->when(strlen($updateStartTime) && isFormatDate($updateStartTime, "Y-m-d"),
                                              function ($query) use ($updateStartTime) {
                                                  $query->where('updatedAt', ">", strtotime($updateStartTime));
                                              }
                                          )
                                          ->when(strlen($updateEndTime) && isFormatDate($updateEndTime, "Y-m-d"),
                                              function ($query) use ($updateEndTime) {
                                                  $query->where('updatedAt', "<",
                                                      strtotime('+1 day', strtotime($updateEndTime)));
                                              }
                                          )
                                          ->orderBy('id', 'desc')
                                          ->paginate($pageSize, ['*'], 'page', $pageNum);

            return [
                'count'    => $pagination->total(),
                'lists'    => MybankEntryListResource::collection($pagination->items()),
                'pageNum'  => $pageNum,
                'pageSize' => $pageSize
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取商户列表失败");
        }
    }

    /**
     * @param int $id
     * @return array|void
     * @throws BaseException
     */
    public static function detail(int $id)
    {
        try {
            return [
                "detail" => MybankEntryDetailResource::collection([
                    MybankEntryModel::query()->with([
                        "shopBindRel" => function ($query) {
                            $query->with(["shopRel"]);
                        }
                    ])->where("id", $id)->first()
                ])->offsetGet(0)
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取商户详情失败");
        }
    }

    /**
     * @param object $params
     * @return array|void
     * @throws BaseException
     * @throws ContainerExceptionInterface
     * @throws GuzzleException
     * @throws NotFoundExceptionInterface
     */
    public static function create(object $params)
    {
        try {

            $from = $params->from ?? 'web';

            $orderNoGenerateFunc = function (string $prefix) {
                return vsprintf("%s%s%s", [
                    $prefix,
                    date("Ymd", time()),
                    str_pad(
                        floor(microtime(true) * 1000) - strtotime(date('Y-m-d 00:00:00'), time()) * 1000,
                        8,
                        "0",
                        STR_PAD_LEFT
                    )
                ]);
            };

            $outMerchantId = $orderNoGenerateFunc("OMI");
            $outOrderNo    = $orderNoGenerateFunc("OON");

            $bindShopList = $params->bindShopList;
            unset($params->bindShopList);
            unset($params->from);

            $merchantType = $params->merchantType;
            $BussAuthType = '12';
            $bussAuthNo   = $params->bussAuthNo ?? "";
            $bussAuthVld  = $params->bussAuthVld ?? "";
            if ($merchantType == MybankEntryModel::MERCHANT_TYPE_01) {// 自然人
                $BussAuthType = '';
                $bussAuthNo   = '';
                $bussAuthVld  = '';
            }

            $certPhotoA = $certPhotoB = $licensePhoto = "";
            list($certPhotoA, $certPhotoB, $licensePhoto) = self::transPreRegisterFile(
                $certPhotoA,
                $certPhotoB,
                $licensePhoto,
                $params
            );

            $uid              = User::query()->where("mobile", $params->principalMobile)->first()->id;
            $myBankEntryModel = MybankEntryModel::query()->create(array_merge((array)$params, [
                "userId"            => $uid,
                "outOrderNo"        => $outOrderNo,
                "outMerchantId"     => $outMerchantId,
                "bussAuthType"      => "12",
                "principalCertType" => "01",
                "contactName"       => $params->principalPerson,
                "contactMobile"     => $params->principalMobile,
                "status"            => $status = MybankEntryModel::STATUS_1,
                "createdAt"         => time(),
                "updatedAt"         => time(),
                "createdByUid"      => request()->user()->id ?? 0,
                "updatedByUid"      => request()->user()->id ?? 0,
            ]));

            MybankShopBindModel::query()->insert(collect($bindShopList)->map(function (int $shopId) use (
                $myBankEntryModel
            ) {
                return [
                    "shopId"       => $shopId,
                    "entryId"      => $myBankEntryModel->id,
                    "createdAt"    => time(),
                    "updatedAt"    => time(),
                    "createdByUid" => request()->user()->id ?? 0,
                    "updatedByUid" => request()->user()->id ?? 0,
                ];
            })->values()->toArray());

            try {
                $preRegisterResponse = MybankRegisterRemote::preRegister([
                    'IsvOrgId'       => Remote::getIsvOrgId(),
                    'OutTradeNo'     => $outOrderNo,
                    'OutMerchantId'  => $outMerchantId, // 商户号
                    'MerchantType'   => $params->merchantType, //商户类型。可选值：01：自然人02: 个体工商户03: 企业商户
                    'MerchantName'   => $params->merchantName,
                    'DealType'       => '01', // 商户经营类型。可选值：01:实体特约商户02:网络特约商户03:实体兼网络特约商户
                    'CertPhotoA'     => $certPhotoA,
                    'CertPhotoB'     => $certPhotoB,
                    'LicensePhoto'   => $licensePhoto,
                    'MerchantDetail' => base64_encode(json_encode([
                        'ContactMobile'     => $params->principalMobile,
                        // 联系人手机号。为商户常用联系人联系手机号。
                        'ContactName'       => $params->principalPerson,
                        // 联系人姓名。为商户常用联系人姓名。
                        'PrincipalMobile'   => $params->principalMobile,
                        // 负责人手机号。开户发送的短信验证码的手机号需要和负责人手机号一致。自然人和个体户必传，企业非必传
                        'PrincipalCertType' => '01',
                        // 负责人证件类型。01：身份证
                        'PrincipalCertNo'   => $params->principalCertNo,
                        // 负责人证件号码。备注：若自然人送自然人证件号码、若个体工商户送经营者证件号码、若企业送法人代表证件号码。
                        'PrincipalPerson'   => $params->principalPerson,
                        // 负责人名称或企业法人代表姓名。备注：若商户类型为“自然人”填写责任人本人，若为“个体工商户”填写经营者本人，若为“企业”填写企业法人代表名称
                        'BussAuthType'      => $BussAuthType,
                        // 证件类型 若商户类型为“个体工商户”、“企业”需要填写证件类型 12：营业执照/统一社会信用代码 41：政府机关证件 42：部队证件 43：社会团体证件 44：事业单位证件 45：民办非企业组织证件 99：其它企业类型证件 备注： 个体工商户：选择12 企业：选择12
                        'BussAuthNo'        => $bussAuthNo,
                        'BussAuthVld'       => $bussAuthVld,
                    ])),
                ]);
            } catch (\Exception $exception) {
                //商户置为已删除
                (clone $myBankEntryModel)->setRawAttributes([
                    "status"       => MybankEntryModel::STATUS_0,
                    "isDeleted"    => 1,
                    "updatedAt"    => time(),
                    "updatedByUid" => request()->user()->id ?? 0
                ])->save();
                //商户关联门店置为已删除
                MybankShopBindModel::query()->where("entryId", $myBankEntryModel->id)->update([
                    "isDeleted"    => 1,
                    "updatedAt"    => time(),
                    "updatedByUid" => request()->user()->id ?? 0
                ]);
                throw $exception;
            }

            (clone $myBankEntryModel)->setRawAttributes([
                "orderNo"      => $preRegisterResponse["response"]["body"]["OrderNo"],
                "updatedAt"    => time(),
                "updatedByUid" => request()->user()->id ?? 0
            ])->save();

            if ($from == 'web') {
                $templateCode = config('sms.ali.mybankPreRegister');
                SmsService::getClient()->send($params->principalMobile, $templateCode, []);
            }

            return [
                "id"         => $myBankEntryModel->id,
                "orderNo"    => $preRegisterResponse["response"]["body"]["OrderNo"],
                "outOrderNo" => $outOrderNo,
                'activeUrl'  => MybankEntryDetailResource::getActiveUrl(MybankEntryModel::STATUS_2,
                    MybankEntryModel::REGISTER_STATUS_0, $outMerchantId, $params->principalMobile),
            ];
        } catch (\Exception $exception) {
            handleException($exception, "创建商户失败");
        }
    }

    /**
     * @param string $certPhotoA
     * @param string $certPhotoB
     * @param string $licensePhoto
     * @param object $params
     * @return string[]
     * @throws BaseException
     * @throws GuzzleException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public static function transPreRegisterFile(
        string &$certPhotoA,
        string &$certPhotoB,
        string &$licensePhoto,
        object $params
    ): array {
        collect([
            ["type" => "01", "file" => attributeGet($params, "identificationCardFrontImg", "")],
            ["type" => "02", "file" => attributeGet($params, "identificationCardBackImg", "")],
            ["type" => "03", "file" => attributeGet($params, "businessLicenseImg", "")],
        ])->each(function ($row) use (&$certPhotoA, &$certPhotoB, &$licensePhoto, $params) {
            if ($row["file"]) {

                $path = $row['file'];

                // 格式化的样式
                $style = 'image/resize,w_1024/quality,q_80/format,jpg';

                if ($row["type"] == "03") {
                    $formatUrl = 'https://file.51kuafu.com/' . $row['file'] . '?x-oss-process=' . $style;
                } else {
                    $formatUrl = privateFilePath($path, $style);
                }

                Log::info("transPreRegisterFile: {$path} - {$formatUrl}");

                // 写入本地临时文件
                $filePath = sys_get_temp_dir() . '/' . Str::random() . '.jpg';
                file_put_contents($filePath, file_get_contents($formatUrl));

                // 上传
                $response = MybankUploadRemote::uploadPhoto($filePath, $row["type"]);
                switch ($row["type"]) {
                    case "01":
                        $certPhotoA = $response["response"]["body"]["PhotoUrl"];
                        break;
                    case "02":
                        $certPhotoB = $response["response"]["body"]["PhotoUrl"];
                        break;
                    case "03":
                        $licensePhoto = $response["response"]["body"]["PhotoUrl"];
                        break;
                }
            }
        });

        return [$certPhotoA, $certPhotoB, $licensePhoto];
    }

    /**
     * @param object $params
     * @throws BaseException
     */
    public static function update(object $params)
    {
        try {
            $myBankEntryId = $params->id;

            $bindShopList = $params->bindShopList;
            unset($params->bindShopList);

            MybankEntryModel::query()->where("id", $myBankEntryId)->update([
                "updatedAt"    => time(),
                "updatedByUid" => request()->user()->id
            ]);

            OperationLogQuery::del(
                "删除绑定门店",
                MybankShopBindModel::class,
                function ($query) use ($myBankEntryId) {
                    $query->where("entryId", $myBankEntryId);
                },
                $operationLog
            );

            OperationLogQuery::insert(
                "绑定门店信息",
                MybankShopBindModel::class,
                collect($bindShopList)->map(function (int $shopId) use ($myBankEntryId) {
                    return [
                        "shopId"       => $shopId,
                        "entryId"      => $myBankEntryId,
                        "createdAt"    => time(),
                        "updatedAt"    => time(),
                        "createdByUid" => request()->user()->id,
                        "updatedByUid" => request()->user()->id,
                    ];
                })->values()->toArray(),
                $operationLog
            );

            event(new OperationLogEvent(request(), OperationLogRecordModel::MODULE_TYPE_1, "网商信息变更",
                $operationLog));

        } catch (\Exception $exception) {
            handleException($exception, "更新商户信息失败");
        }
    }


    /**
     * @param int $id
     * @throws BaseException
     */
    public static function remindActivate(int $id)
    {
        try {
            $mybankEntryModel = MybankEntryModel::query()->where("id", $id)->first();
            $templateCode     = config('sms.ali.mybankPreRegister');
            SmsService::getClient()->send($mybankEntryModel->principalMobile, $templateCode, []);
        } catch (\Exception $exception) {
            handleException($exception, "提醒开户失败");
        }
    }

    /**
     * @param int $id
     * @throws BaseException
     */
    public static function remindWithholding(int $id)
    {
        try {
            $mybankEntryModel = MybankEntryModel::query()->where("id", $id)->first();
            $templateCode     = config('sms.ali.mybankWithholding');
            SmsService::getClient()->send($mybankEntryModel->principalMobile, $templateCode, []);
        } catch (\Exception $exception) {
            handleException($exception, "提醒签约失败");
        }
    }

    public static function freeze()
    {
        try {

        } catch (\Exception $exception) {
            handleException($exception, "冻结商户失败");
        }
    }

    public static function status()
    {
        try {

        } catch (\Exception $exception) {
            handleException($exception, "禁用｜启用 商户失败");
        }
    }

    /**
     * 预入驻回调
     *
     * @param object $requestBody
     */
    public static function preRegisterAsyncNotify(object $requestBody)
    {
        $outOrderNo = attributeGet($requestBody, "OutTradeNo", "");
        logMsg("网商回调事件 - 预入住回调处理", ["outOrderNo" => $outOrderNo]);
        if (
            $outOrderNo
            &&
            $mybankEntryModel = MybankEntryModel::query()->where("status", MybankEntryModel::STATUS_1)
                                                ->where("outOrderNo", $requestBody->OutTradeNo)
                                                ->first()
        ) {
            logMsg("网商回调事件 - 预入住回调处理 - 1", ["outOrderNo" => $outOrderNo]);
            $mybankEntryModel->update(array_merge([
                "updatedAt"             => time(),
                "merchantId"            => attributeGet($requestBody, "MerchantId", ""),
                "preRegisterStatus"     => $requestBody->RegisterStatus,
                "preRegisterFailReason" => is_array($requestBody->FailReason) ? json_encode($requestBody->FailReason,
                    JSON_UNESCAPED_UNICODE) : $requestBody->FailReason,
            ],
                $requestBody->RegisterStatus == MybankEntryModel::PRE_REGISTER_STATUS_1 ? ["status" => MybankEntryModel::STATUS_2] : []));

            // nsq推送注册成功消息
            $param = [
                'entry' => $mybankEntryModel->toArray(),
            ];
            (new NsqPublisher(config('nsq.default.address')))->pub(NsqCodeEnum::MYBANK_REGISTER, $param);
        }
    }

    /**
     * 激活回调
     *
     * @param object $requestBody
     */
    public static function preRegisterActiveAsyncNotify(object $requestBody)
    {
        $merchantId = attributeGet($requestBody, "MerchantId", "");
        if (
            $merchantId
            &&
            $mybankEntryModel = MybankEntryModel::query()->where("status", MybankEntryModel::STATUS_2)
                                                ->where("merchantId", $merchantId)
                                                ->first()
        ) {
            $mybankEntryModel->update(array_merge([
                "updatedAt"          => time(),
                "registerStatus"     => $requestBody->RegisterStatus,
                "registerFailReason" => is_array($requestBody->FailReason) ? json_encode($requestBody->FailReason,
                    JSON_UNESCAPED_UNICODE) : $requestBody->FailReason,
            ],
                $requestBody->RegisterStatus == MybankEntryModel::REGISTER_STATUS_1 ? [
                    "status" => MybankEntryModel::STATUS_3
                ] : []));

            // nsq推送注册成功消息
            $param = [
                'entry' => $mybankEntryModel->toArray(),
            ];
            (new NsqPublisher(config('nsq.default.address')))->pub(NsqCodeEnum::MYBANK_REGISTER, $param);
        }
    }

    /**
     * 协议代扣 - 回调
     *
     * @param object $requestBody
     */
    public static function arrangementNotify(object $requestBody)
    {
        $merchantId = attributeGet($requestBody, "MerchantId", "");
        if (
            $merchantId
            &&
            $mybankEntryModel = MybankEntryModel::query()
                                                ->whereIn(
                                                    "status",
                                                    [MybankEntryModel::STATUS_3, MybankEntryModel::STATUS_2]
                                                )->where("merchantId", $requestBody->MerchantId)
                                                ->first()
        ) {
            $arrangementStatus = attributeGet($requestBody, "ArrangementStatus", null);
            switch ($arrangementStatus) {
                case "NOT_EXIST"://不存在
                    break;
                case "UN_VALID"://已申请，未生效
                    $mybankEntryModel->update([
                        "updatedAt"      => time(),
                        "withholdStatus" => MybankEntryModel::WITHHOLD_STATUS_2
                    ]);
                    break;
                    break;
                case "VALID"://已生效
                    $mybankEntryModel->update([
                        "updatedAt"      => time(),
                        "arrangementNo"  => attributeGet($requestBody, "ArrangementNo", ""),
                        "withholdStatus" => MybankEntryModel::WITHHOLD_STATUS_3,
                        "withholdOpenAt" => time()
                    ]);
                    break;
                case "INVALID_TO_BE_CONFIRM"://解约中待平台确认
                    $mybankEntryModel->update([
                        "arrangementNo"  => attributeGet($requestBody, "ArrangementNo", ""),
                        "updatedAt"      => time(),
                        "withholdStatus" => MybankEntryModel::WITHHOLD_STATUS_4
                    ]);
                    break;
            }
        }
    }

    /**
     *  解约申请审核确认接口
     *
     * @param object $params
     * @throws BaseException
     */
    public static function confirmArrangement(object $params)
    {
        try {
            $mybankEntryModel = MybankEntryModel::query()->where("id", $params->id)->first();
            MybankRegisterRemote::arrangementAudit(
                $mybankEntryModel->arrangementNo,
                $mybankEntryModel->merchantId,
                $params->auditResult
            );

            if ($params->auditResult == "AGREE") {
                $mybankEntryModel->update([
                    "withholdStatus"  => MybankEntryModel::WITHHOLD_STATUS_1,
                    "withholdCloseAt" => time(),
                    "updatedAt"       => time()
                ]);
            } else {
                $mybankEntryModel->update([
                    "withholdStatus" => MybankEntryModel::WITHHOLD_STATUS_3,
                    "updatedAt"      => time()
                ]);
            }

        } catch (\Exception $exception) {
            handleException($exception, "解除授权操作失败");
        }
    }

    /**
     * 校验用户是否拥有网商权限
     *
     * @param int $userId
     * @return bool
     */
    public static function checkUserMybankAccess(int $userId): bool
    {
        return MybankEntryModel::query()->where('userId', $userId)->exists();
    }

    /**
     * 注册情况
     * @param int $userId
     * @return array
     */
    public static function registerStatus(int $userId): array
    {
        $statusText = function ($status) {
            return ['status' => $status, 'text' => MybankEntryModel::STATUS_TEXT[$status] ?? ''];
        };

        // 是否注册
        $user = MybankEntryModel::query()->where('userId', $userId)->orderByDesc('id')->first();
        if (!$user) {
            return $statusText(MybankEntryModel::STATUS_0);
        }

        // 注册中
        if (in_array($user->status, [MybankEntryModel::STATUS_1, MybankEntryModel::STATUS_2])) {
            return $statusText(MybankEntryModel::STATUS_1);
        }

        // 已注册
        if ($user->status == MybankEntryModel::STATUS_3) {
            return $statusText(MybankEntryModel::STATUS_3);
        }

        // 未注册
        return $statusText(MybankEntryModel::STATUS_0);
    }
}
